#include "Generate.h"
#import "RSReplay.h"
#import "RSPlayerData.h"
#import "RSChatMessage.h"
#import <AppKit/AppKit.h>

static CGImageRef noPreviewAvailable;

static NSString* bnetDomains[] = {
	nil,
	@"us.battle.net",
	@"eu.battle.net",
	@"sea.battle.net",
	@"kr.battle.net",
	@"tw.battle.net",
	@"www.battlenet.com.cn"
};

static NSString* bnetLink = @"http://%@/sc2/en/profile/%u/1/%@/";

static void attach(NSMutableDictionary* attachments, NSString* type, NSString* fileName, NSData* data)
{
	NSDictionary* attachment = [NSMutableDictionary dictionaryWithObjectsAndKeys:type, (NSString *)kQLPreviewPropertyMIMETypeKey, data, (NSString *)kQLPreviewPropertyAttachmentDataKey, nil];
	[attachments setObject:attachment forKey:fileName];
}

static void attach(NSMutableDictionary* attachments, NSString* type, NSString* fileName)
{
	NSBundle* currentBundle = [NSBundle bundleWithIdentifier:@"com.felixcloutier.Starcraft-Replay-QL"];
	NSString* path = [currentBundle pathForResource:[fileName stringByDeletingPathExtension] ofType:[fileName pathExtension]];
	NSData* data = [NSData dataWithContentsOfFile:path];
	attach(attachments, type, fileName, data);
}

static NSData* imageToPNG(CGImageRef image)
{
	NSMutableData* output = [NSMutableData data];
	CGImageDestinationRef pngDest = CGImageDestinationCreateWithData((CFMutableDataRef)output, CFSTR("public.png"), 1, NULL);
	CGImageDestinationAddImage(pngDest, image, NULL);
	CGImageDestinationFinalize(pngDest);
	return output;
}

static NSString* resourceContents(NSString* fileName)
{
	NSBundle* currentBundle = [NSBundle bundleWithIdentifier:@"com.felixcloutier.Starcraft-Replay-QL"];
	NSString* path = [currentBundle pathForResource:[fileName stringByDeletingPathExtension] ofType:[fileName pathExtension]];
	return [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
}

static NSString* safeXML(id input)
{
	NSMutableString* string = [[[input description] mutableCopy] autorelease];
	[string replaceOccurrencesOfString:@"<" withString:@"&lt;" options:0 range:NSMakeRange(0, string.length)];
	[string replaceOccurrencesOfString:@">" withString:@"&gt;" options:0 range:NSMakeRange(0, string.length)];
	[string replaceOccurrencesOfString:@"&" withString:@"&amp;" options:0 range:NSMakeRange(0, string.length)];
	return string;
}

static CGImageRef loadDefaultPreview()
{
	if (noPreviewAvailable == NULL)
	{
		NSBundle* currentBundle = [NSBundle bundleWithIdentifier:@"com.felixcloutier.Starcraft-Replay-QL"];
		NSString* path = [currentBundle pathForImageResource:@"no-preview.png"];
		NSData* data = [NSData dataWithContentsOfFile:path];
		CGDataProviderRef pngData = CGDataProviderCreateWithCFData((CFDataRef)data);
		noPreviewAvailable = CGImageCreateWithPNGDataProvider(pngData, NULL, YES, kCGRenderingIntentDefault);
		CGDataProviderRelease(pngData);
	}
	return noPreviewAvailable;
}

// use the map preview as the thumbnail
OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
{
	NSString* htmlBase = resourceContents(@"body.html");
	NSString* htmlTeamBody = resourceContents(@"team-body.html");
	NSString* htmlTeamPlayer = resourceContents(@"team-player.html");
	NSString* htmlBnetLink = resourceContents(@"bnet-link.html");
	NSString* htmlAiMention = resourceContents(@"ai-mention.html");
	NSString* chatLogRow = resourceContents(@"chat-log-row.html");
	NSString* cssStyle = resourceContents(@"style.css");
	
	NSURL* nsUrl = (NSURL*)url;
	RSReplay* replay = [[RSReplay alloc] initWithContentsOfFile:nsUrl.path];
	
	unsigned gameDuration = [replay.endDate timeIntervalSinceDate:replay.startDate];
	unsigned hours = gameDuration / 3600;
	unsigned minutes = (gameDuration % 3600) / 60;
	unsigned seconds = gameDuration % 60;
	NSString* timeAsString = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds];
	
	// teams
	NSMutableString* teamsBody = [NSMutableString string];
	NSArray* teams = replay.teams;
	unsigned largestTeamSize = 0;
	for (int i = 0; i < teams.count; i++)
	{
		NSArray* team = [teams objectAtIndex:i];
		NSMutableString* teamContent = [NSMutableString string];
		for (RSPlayerData* player in team)
		{
			RSReplayColor color = player.color;
			unsigned hexColor = (color.r << 16) | (color.g << 8) | color.b;
			NSString* playerMention = @"";
			if (player.isComputer)
				playerMention = htmlAiMention;
			else
			{
				NSString* url = [NSString stringWithFormat:bnetLink, bnetDomains[player.region], player.uid, player.name];
				playerMention = [NSString stringWithFormat:htmlBnetLink, safeXML(url)];
			}
			[teamContent appendFormat:htmlTeamPlayer, [player.race lowercaseString], playerMention, hexColor, safeXML(player.name)];
		}
		[teamsBody appendFormat:htmlTeamBody, i + 1, teamContent];
		
		largestTeamSize = MAX(largestTeamSize, (unsigned)team.count);
	}
	
	// chat log
	NSMutableString* messages = [NSMutableString string];
	for (RSChatMessage* message in replay.messages)
	{
		RSPlayerData* player = message.player;
		RSReplayColor color = player.color;
		NSString* targetName = message.target == RPReplayMessageTargetAll ? @"All" : @"Allies";
		unsigned hexColor = (color.r << 16) | (color.g << 8) | color.b;
		unsigned flooredTime = message.time;
		unsigned hours = flooredTime / 3600;
		unsigned minutes = (flooredTime % 3600) / 60;
		unsigned seconds = flooredTime % 60;
		NSString* hour = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds];
		[messages appendFormat:chatLogRow, hexColor, hour, targetName, safeXML(player.name), safeXML(message.body)];
	}
	
	NSString* html = [NSString stringWithFormat:htmlBase, safeXML(replay.mapName), timeAsString, teamsBody, messages];
	NSMutableDictionary* htmlProperties = [NSMutableDictionary dictionary];
	[htmlProperties setObject:@"UTF-8" forKey:(NSString*)kQLPreviewPropertyTextEncodingNameKey];
	[htmlProperties setObject:@"application/xhtml+xml" forKey:(NSString*)kQLPreviewPropertyMIMETypeKey];
	
	CGImageRef mapPreview;
	if (replay.mapPreview)
		mapPreview = replay.mapPreview;
	else
		mapPreview = loadDefaultPreview();
		
	NSSize previewSize = NSMakeSize(CGImageGetWidth(replay.mapPreview), CGImageGetHeight(replay.mapPreview));
	
	unsigned width = previewSize.width;
	unsigned height = previewSize.height;
	float resizeBy = MAX(width, height) / 256.0;
	
	if (resizeBy > 1)
	{
		width /= resizeBy;
		height /= resizeBy;
	}
	
	NSString* stylesheetAsString = [NSString stringWithFormat:cssStyle, width, height, (256 - width) / 2, (256 - height) / 2];
	NSData* stylesheet = [stylesheetAsString dataUsingEncoding:NSUTF8StringEncoding];
	
	NSMutableDictionary* attachments = [NSMutableDictionary dictionary];
	attach(attachments, @"image/png", @"terran.png");
	attach(attachments, @"image/png", @"protoss.png");
	attach(attachments, @"image/png", @"zerg.png");
	attach(attachments, @"image/png", @"random.png");
	attach(attachments, @"image/png", @"battlenet.png");
	attach(attachments, @"image/png", @"trophy.png");
	attach(attachments, @"text/javascript", @"jquery-1.7.min.js");
	attach(attachments, @"text/javascript", @"support.js");
	attach(attachments, @"text/css", @"style.css", stylesheet);
	attach(attachments, @"image/png", @"preview.png", imageToPNG(mapPreview));
	[htmlProperties setObject:attachments forKey:(NSString*)kQLPreviewPropertyAttachmentsKey];
	
	// how big should the window be?
	unsigned windowWidth = 256 + MIN(4, (unsigned)teams.count) * 200 + 50; // 50 px added as padding
	unsigned windowHeight = 90 + MAX(256, 25 * largestTeamSize);
	[htmlProperties setObject:[NSNumber numberWithUnsignedInt:windowWidth] forKey:(NSString*)kQLPreviewPropertyWidthKey];
	[htmlProperties setObject:[NSNumber numberWithUnsignedInt:windowHeight] forKey:(NSString*)kQLPreviewPropertyHeightKey];
	
	NSData* htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];
	QLPreviewRequestSetDataRepresentation(preview, (CFDataRef)htmlData, kUTTypeHTML, (CFDictionaryRef)htmlProperties);
	
	[replay release];
		
    return noErr;
}

void CancelPreviewGeneration(void* thisInterface, QLPreviewRequestRef preview)
{
    // unsupported
}
